﻿using System;
using System.Net;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace BlackJack
{
    public class BlackJackHand : IEnumerable, IList
    {
        #region Private Members
        
        private Card[] m_cards;
        private Thickness m_handLocation;
        private int m_cardPosition;
        private bool m_doubled;
        private int NOOFCARDSRESIZEAMOUNT = 5;
        private double m_bet;

        #endregion

        #region Methods
        public BlackJackHand()
        {
            m_cards = new Card[NOOFCARDSRESIZEAMOUNT];
            m_cardPosition = 0;
            m_doubled = false;
            m_bet = 0;
        }

        public BlackJackHand(Thickness location)
        {
            m_cards = new Card[NOOFCARDSRESIZEAMOUNT];
            m_cardPosition = 0;
            m_doubled = false;
            m_handLocation = location;
            m_bet = 0;
        }

        public Image[] DrawHand()
        {
            double leftVal = 0;
            Image[] cardImgList = new Image[m_cards.Length];
            int i=0;
            foreach (Card c in m_cards)
            {
                if (c != null)
                {
                    Thickness newVal = m_handLocation;
                    newVal.Left += leftVal;
                    c.Margin = newVal;
                    cardImgList[i] = c.DrawCard();
                    leftVal += 2.0;                    
                    i++;
                }
            }
            return cardImgList;
        }


        public bool IsBlackJack()
        {
            return (this.Total() == 21 && m_cards.Length == 2);
        }

        public bool IsSoft()
        {
            //determine if the total is soft
            int total = 0;
            int aceCount = 0;
            foreach (Card c in m_cards)
            {
                if (c != null)
                {
                    switch (c.FaceValue)
                    {
                        case Card.CardType.Ace:
                            {
                                aceCount++;
                                total += 11;
                                break;
                            }
                        default:
                            {
                                total += c.TrueValue;
                                break;
                            }
                    }
                }
            }

            while (total > 21 || aceCount == 0)
            {
                aceCount--;
                total -= 10;
            }
            return aceCount > 0;
        }

        public int Total()
        {
            int total = 0;
            int aceCount = 0;
            foreach (Card c in m_cards)
            {
                if (c != null)
                {
                    switch (c.FaceValue)
                    {
                        case Card.CardType.Ace:
                            {
                                aceCount++;
                                total += 11;
                                break;
                            }
                        default:
                            {
                                total += c.TrueValue;
                                break;
                            }
                    }
                }
            }

            while (total > 21 || aceCount == 0)
            {
                aceCount--;
                total -= 10;               
            }
            return total;
        }

        public OutcomeType OutcomeDecision(BlackJackHand dealerHand)
        {
            int dealerHandTotal = dealerHand.Total();
            int thisHandTotal = this.Total();

            if ( thisHandTotal > 21 || dealerHandTotal > thisHandTotal)
                return OutcomeType.Lost;
            else if ( dealerHandTotal>21 || thisHandTotal > dealerHandTotal)
                return OutcomeType.Won;
            else if (this.IsBlackJack() && !dealerHand.IsBlackJack())
                return OutcomeType.Blackjack;
            else if (dealerHandTotal == thisHandTotal)
                return OutcomeType.Push;
            return OutcomeType.None;
        }

        int IList.Add(object obj)
        {
            Card c = (Card)obj;
            if (c == null)
            {
                throw new InvalidCastException("Cannot cast the object as a card object");
            }
            return Add(c);
        }

        public int Add(Card c)
        {
            if (m_cards.Length == NOOFCARDSRESIZEAMOUNT - 1)
            {
                //resize the array
                Card[] temp = new Card[NOOFCARDSRESIZEAMOUNT];
                Array.Copy(m_cards, 0, temp, 0, m_cards.Length);
                m_cards = temp;
            }

            m_cards[m_cardPosition++] = c;
            return m_cardPosition;
        }

        void IList.Insert(int index, Object card)
        {
            Card crd = card as Card;

            if (crd == null)
                throw new InvalidCastException("You can only add Cards to a hand.");

            Insert(index, crd);
        }

        void Insert(int index, Card card)
        {
            if (index < 0 || index > m_cardPosition)
                throw new System.IndexOutOfRangeException("index");

            int newSize;

            if (m_cardPosition == m_cards.Length - 1)
                newSize = m_cardPosition + NOOFCARDSRESIZEAMOUNT;
            else
                newSize = m_cardPosition;

            Card[] tmpCards = new Card[newSize];

            Array.Copy(m_cards, 0, tmpCards, 0, index);
            tmpCards[index] = card;
            index++;
            Array.Copy(m_cards, index, tmpCards, index, m_cards.Length - index);

            m_cards = tmpCards;
            m_cardPosition++;
        }


        bool IList.Contains(Object card)
        {
            Card crd = card as Card;

            if (crd == null)
                return false;
            else
                return Contains(crd);
        }

        public bool Contains(Card c1)
        {
            foreach (Card c in m_cards)
            {
                if (c1.Equals(c))
                    return true;
            }
            return false;
        }

        int IList.IndexOf(Object obj)
        {
            Card c = (Card)obj;
            if (c == null)
            {
                throw new InvalidCastException("Cannot cast the object as a card object");
            }
            return IndexOf(c);
        }

        public int IndexOf(Card c1)
        {
            for (int i =0; i < m_cardPosition; i++)
            {
                if (c1.Equals(m_cards[i]))
                    return i;
            }
            return -1;
        }

        void IList.Remove(Object card)
        {
            Card crd = card as Card;

            if (crd != null)
                Remove(crd);
        }
        public void Remove(Card c)
        {
            int index = IndexOf(c);
            RemoveAt(index);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }

        public CardEnum GetEnumerator()
        {
            return new CardEnum(m_cards);
        }
        public void RemoveAt(int index)
        {            
            if (index >= 0 && index < m_cardPosition)
            {
                for (int i = index; i < m_cardPosition - 1; i++)
                {
                    m_cards[i] = m_cards[i + 1];
                }
                m_cards[m_cardPosition - 1] = null;
            }
        }

        public void Clear()
        {
            m_cards = new Card[NOOFCARDSRESIZEAMOUNT];
            m_cardPosition = 0;
        }

        public void CopyTo(Array list, int i)
        {
            if (i < 0)
                throw new IndexOutOfRangeException();
            if (list == null)
                throw new ArgumentNullException();
            if (list.Length >= i || list.Length - i < m_cards.Length)
                throw new ArgumentException();

            Array.Copy(m_cards, 0, list, i, m_cards.Length);
        }
        #endregion

        #region Properties

        public Thickness HandLocation
        {
            get { return m_handLocation; }
            set { m_handLocation = value; }
        }

        public double Bet
        {
            get { return m_bet; }
            set { m_bet = value; }
        }

        object IList.this[int index]
        {
            get 
            {
                if (index < 0 || index > m_cardPosition)
                    throw new IndexOutOfRangeException();
                else
                    return m_cards[index]; 
            }
            set 
            {
                if (index < 0 || index > m_cardPosition)
                    throw new IndexOutOfRangeException();
                else
                    m_cards[index] = (Card)value; 
            }

        }
        public int Count
        {
            get { return m_cardPosition; }
        }
        public Object SyncRoot
        {
            get { return m_cards.SyncRoot; }
        }
        public bool IsSynchronized
        {
            get { return true; }
        }
        public bool IsFixedSize
        {
            get { return false; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public Card[] Cards
        {
            get { return m_cards; }
        }

        #endregion

        #region NestedType
        public enum OutcomeType
        {
            None = 0,
            Won = 1,
            Lost = 2,
            Push = 3,
            Blackjack = 4
        }

        public class CardEnum : IEnumerator
        {
            public Card[] m_cards;
            private int m_position = -1;
            public CardEnum(Card[] cards)
            {
                m_cards = cards;
            }

            public void Reset()
            {
                m_position = -1;
            }

            public Object Current
            {
                get
                {
                    try
                    {
                        return m_cards[m_position];
                    }
                    catch (IndexOutOfRangeException ex)
                    {
                        throw ex;
                    }
                }
            }

            public bool MoveNext()
            {
                m_position++;
                return (m_position < m_cards.Length);
            }
        }
        #endregion
    }
}
